﻿using UnityEngine;
using UnityEditor;
using Excel;
using System.IO;
using System.Data;
using System;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using LitJson;
using System.Linq;
using System.Text;

namespace Simulation.Framework
{

    public class ExcelCoverWindow : EditorWindow
    {
        private static string codeOutPath;
        private static string excelInputPath;
        private static string jsonOutputPath;
        private static bool isSingleExcelBook;

        private bool isLoadExcel;

        // config
        private static int firstSheet;          // 第一张表的索引

        [MenuItem("SimulationFramework/Excel Cover")]
        public static void Cover() {
            GetWindow<ExcelCoverWindow>("excel cover");
        }

        private void OnEnable()
        {
            isSingleExcelBook = true;
            codeOutPath = string.Empty;
            excelInputPath = string.Empty;
            jsonOutputPath = string.Empty;
            firstSheet = 0;

            LoadConfig();
        }

        #region gui

        private void OnGUI() {
            ExcelFormatGUI();
            ExcelPathGUI();
            CodePathGUI();
            JsonPathGUI();
            CreateButton();
        }

        private void CodePathGUI() {
            if (!isLoadExcel) return;
            GUILayout.BeginHorizontal();

            codeOutPath = GUILayout.TextField(codeOutPath,GUILayout.Width(200));
            if (GUILayout.Button("数据结构路径", GUILayout.Width(100))) 
                codeOutPath = EditorUtility.OpenFolderPanel("存储数据结构", Application.dataPath, "") + "/";

            GUILayout.EndHorizontal();
        }

        private void ExcelPathGUI() {
            GUILayout.BeginHorizontal();

            excelInputPath = GUILayout.TextField(excelInputPath, GUILayout.Width(200));
            if (GUILayout.Button("选择 excel", GUILayout.Width(100)))
                excelInputPath = EditorUtility.OpenFolderPanel("选择 excel ", Application.dataPath, "") + "/";
            isLoadExcel = IsActivePath(excelInputPath);
            if (!isLoadExcel) excelInputPath = string.Empty;
            GUILayout.EndHorizontal();
        }

        private void JsonPathGUI() {
            if (!isLoadExcel) return;
            GUILayout.BeginHorizontal();

            jsonOutputPath = GUILayout.TextField(jsonOutputPath, GUILayout.Width(200));
            if (GUILayout.Button("json 路径", GUILayout.Width(100)))
                jsonOutputPath = EditorUtility.OpenFolderPanel("选择路径", Application.dataPath, "") + "/";

            GUILayout.EndHorizontal();
        }

        private void ExcelFormatGUI() {
            GUILayout.BeginHorizontal();

            if (GUILayout.Toggle(isSingleExcelBook, "单文件输出"))
            {
                isSingleExcelBook = true;
                firstSheet = 1;
            }

            if (GUILayout.Toggle(!isSingleExcelBook, "多文件输出"))
            {
                isSingleExcelBook = false;
                firstSheet = 0;
            }

            GUILayout.EndHorizontal();
        }

        private void CreateButton() {
            if (!isLoadExcel) return;

            GUILayout.BeginHorizontal();

            if (GUILayout.Button("创建 json")) {
                LoadExcels();
                SaveConfig();
                AssetDatabase.Refresh();
            }

            GUILayout.EndHorizontal();
        }

        #endregion

        private void SaveConfig() {
            string path = Directory.GetCurrentDirectory();
            string file = path + "/cache";
            StringBuilder stringBuilder = new StringBuilder();
            string conetnt = stringBuilder.AppendLine("excel&" + excelInputPath.Replace(Application.dataPath,"")).
                AppendLine("code&" + codeOutPath.Replace(Application.dataPath, "")).
                AppendLine("json&" + jsonOutputPath.Replace(Application.dataPath, "")).ToString();
            File.WriteAllText(file,conetnt);
        }

        private void LoadConfig() {
            string path = Directory.GetCurrentDirectory();
            string file = path + "/cache";
            if (!File.Exists(file)) return;

            string[] allLines = File.ReadAllLines(file);
            // excel 路径
            excelInputPath = Application.dataPath + allLines[0].Split('&')[1];
            // 数据结构输出路径
            codeOutPath = Application.dataPath + allLines[1].Split('&')[1];
            // json 输出路径
            jsonOutputPath = Application.dataPath + allLines[2].Split('&')[1];
        }

        private static void LoadExcels() {
            DirectoryInfo directoryInfo = new DirectoryInfo(excelInputPath);
            FileInfo[] fileInfos = directoryInfo.GetFiles();
            if (fileInfos == null) return;
            for (int i = 0; i < fileInfos.Length; i++)
            {
                FileInfo fileInfo = fileInfos[i];
                string extensions = fileInfo.Extension;
                if (extensions != ".xls" && extensions != ".xlsx")
                    continue;
                ReadExcel(fileInfo.FullName);
            }
        }

        private static void ReadExcel(string excelPath) {
            FileStream fileStream = new FileStream(excelPath, FileMode.Open);
            IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(fileStream);

            DataSet dataSet = excelReader.AsDataSet();

            // 从第 firstSheet 开始读取
            JsonData root = new JsonData();
            for (int i = firstSheet; i < dataSet.Tables.Count; i++)
            {
                DataTable dataTable = dataSet.Tables[i];
                string fieldName = dataTable.TableName;
                try
                {
                    JsonData jsonData = ReadTable(dataTable);
                    // 创建 文件
                    if (!isSingleExcelBook) WriteJson(jsonData, fieldName);
                    // 存储
                    fieldName = fieldName.First().ToString().ToLower() + fieldName.Substring(1);
                    root[fieldName] = jsonData;

                }
                catch (Exception e)
                {
                    throw new Exception($"文件:{excelPath},表格{fieldName} 读取有误");
                }
            }

            // 如果是单工作簿，创建一个总的 json
            if (isSingleExcelBook)
                WriteJson(root, excelPath);
        }

        private static JsonData ReadTable(DataTable dataTable) {
            DataRowCollection rows = dataTable.Rows;
            string[] types = GetRowValues(rows[0]);
            string[] titles = GetRowValues(rows[2]);

            // 创建类
            CreateCode(dataTable.TableName, titles, types);

            // 创建 json
            JsonData classData = new JsonData();
            classData.SetJsonType(JsonType.Array);
            for (int i = 3; i < rows.Count; i++) {
                JsonData data = new JsonData();
                for (int j = 0; j < titles.Length; j++) {
                    string fieldName = titles[j];
                    object o = rows[i][j];
                    bool reslt = IsCellTypeValid(o);
                    string fieldValue = reslt ? (rows[i][j]).ToString() : string.Empty;
                    // \r\n
                    fieldValue = fieldValue.Replace("\n", "\\n");
                    fieldValue = fieldValue.Replace("\r", "\\r");
                    data[fieldName] =  new JsonData(StringToObj(StringToType(types[j]),fieldValue));
                }
                classData.Add( data);
            }
            return classData;
        }

        private static void WriteJson(JsonData jsonData,string fileName) {
            string json = jsonData.ToJson();
            string path = jsonOutputPath + Path.GetFileNameWithoutExtension(fileName) + ".json";
            File.WriteAllText(path, json, new UTF8Encoding(false));
        }

        private static void CreateCode(string className, string[] fieldNames,string[] fieldTypes)
        {
            CSharpCodeProvider provider = new CSharpCodeProvider();
            CodeCompileUnit compileUnit = new CodeCompileUnit();
            CodeNamespace codeNamesspace = new CodeNamespace();
            CodeTypeDeclaration declara = new CodeTypeDeclaration(className);


            for (int i = 0; i < fieldNames.Length; i++) {
                string fieldName = fieldNames[i];
                Type type = i >= fieldTypes.Length ? typeof(string) : StringToType(fieldTypes[i]);
                CodeMemberField memberField = new CodeMemberField(type, fieldName);
                memberField.Attributes = MemberAttributes.Public;
                declara.Members.Add(memberField);
            }

            codeNamesspace.Types.Add(declara);
            compileUnit.Namespaces.Add(codeNamesspace);

            string filePath = codeOutPath + className + ".cs";
            StreamWriter streamWriter = new StreamWriter(filePath, false, Encoding.UTF8);
            IndentedTextWriter tw = new IndentedTextWriter(streamWriter, "    ");
            provider.GenerateCodeFromCompileUnit(compileUnit, tw,new CodeGeneratorOptions());

            streamWriter.Flush();
            streamWriter.Dispose();
            streamWriter.Close();
        }

        private static string[] GetRowValues(DataRow rowData) {
            if (rowData == null) return null;
            string[] values = new string[rowData.ItemArray.Length];
            for (int i = 0; i < values.Length; i++) {
                object o = rowData.ItemArray[i];
                values[i] = o == null ? string.Empty : rowData.ItemArray[i].ToString();
            }
            return values;
        }

        private static Type StringToType(string str) {
            switch (str)
            {
                case "string":
                case "String":return typeof(String);
                case "int":
                case "Int":return typeof(int);
                case "float":
                case "Float":return typeof(double);
                case "bool":
                case "Bool":return typeof(bool);
                case "double":
                case "Double":return typeof(double);
                default: return typeof(string);
            }
        }


        private static object StringToObj(Type type,string str) {
            if (type == typeof(String))
                return str;
            if (type == typeof(int))
                return int.Parse(str);
            if (type == typeof(float))
                return double.Parse(str);
            if (type == typeof(bool))
                return bool.Parse(str);
            if (type == typeof(double))
                return double.Parse(str);
            return str;
        }

        /// <summary>
        /// 单元格的类型是否有效
        /// </summary>
        private static bool IsCellTypeValid(object o) {
            Type type = o.GetType();
            if (type == typeof(string))
                return true;
            if (type == typeof(int))
                return true;
            if (type == typeof(float))
                return true;
            if (type == typeof(double))
                return true;
            if (type == typeof(bool))
                return true;
            return false;
        }

        /// <summary>
        /// 是否是有效的路径
        /// </summary>
        /// <returns></returns>
        private static bool IsActivePath(string path) {
            if (string.IsNullOrEmpty(path))
                return false;
            // 不存在该路径
            if (!Directory.Exists(path) && !File.Exists(path))
                return false;
            // 路径未指向工程内
            if (!path.Contains(Application.dataPath))
                return false;
            return true;
        }
    }
}